-- only support single frame, 8-bit depth, full range, no sequence, no layer
-- YUV 400/420/422/444; noAlpha/hasAlpha

local format = string.format
local assert = assert
local print = print

local f = io.open(arg[1], "rb")
local s = f:read "*a"
f:close()
local i = 0

local function readByte()
	local a = s:byte(i+1)
	i = i + 1
	return a
end

local function readShortBE()
	local a,b = s:byte(i+1), s:byte(i+2)
	i = i + 2
	return a * 0x100 + b
end

local function readIntBE()
	local a,b,c,d = s:byte(i+1), s:byte(i+2), s:byte(i+3), s:byte(i+4)
	i = i + 4
	return a * 0x1000000 + b * 0x10000 + c * 0x100 + d
end

local function readStr(n)
	if n then
		local s = s:sub(i+1, i+n)
		i = i + n
		return s
	end
	local j = i
	while s:byte(i+1) ~= 0 do
		i = i + 1
	end
	local s = s:sub(j+1, i)
	i = i + 1
	return s
end

local function readTag()
	local n = readIntBE()
	local t = readStr(4)
	return n, t
end

-- 0x00
local n, t = readTag()
print(format("%4X: [%2X] %s", i-8, n, t))
assert(n == 0x20 or n == 0x1c) -- 444/420 or 422/400
assert(t == "ftyp")
-- 0x08
assert(readStr(4) == "avif") -- major_brand (or "avis" for sequence(frames > 1))
-- 0x0c
assert(readIntBE() == 0) -- minor_version
-- 0x10
assert(readStr(0xc) == "avifmif1miaf") -- add "avismsf1iso8" before if sequence
print(format("%4X:      avif, 0, avifmif1miaf", i-20))
-- 0x1c
if n == 0x20 then -- 444/420 && depth==8or10
	t = readStr(4)
	print(format("%4X:      %s", i-4, t))
	assert(t == "MA1A" or t == "MA1B") -- 444 or 420
end

local iMeta = i
local nMeta
nMeta, t = readTag()
print(format("%4X: [%2X] %s", i-8, n, t))
assert(t == "meta")
assert(readIntBE() == 0) -- meta version(1)&flags(3)
print(format("%4X:       0", i-4))

local iHdlr = i
n, t = readTag()
print(format("%4X:    [%2X] %s", i-8, n, t))
assert(t == "hdlr")
assert(readIntBE() == 0) -- hdlr version(1)&flags(3)
assert(readIntBE() == 0) -- pre_defined
assert(readStr(4) == "pict") -- handler_type
assert(readIntBE() == 0) -- reserved
assert(readIntBE() == 0) -- reserved
assert(readIntBE() == 0) -- reserved
assert(readStr() == "") -- handler_name
assert(i - iHdlr == n)
print(format("%4X:         0, 0, pict, 0, 0, 0, ''", i-25))

local iPitm = i
n, t = readTag()
print(format("%4X:    [%2X] %s", i-8, n, t))
assert(t == "pitm")
assert(readIntBE() == 0) -- pitm version(1)&flags(3)
assert(readShortBE() == 1) -- item_ID
assert(i - iPitm == n)
print(format("%4X:         0, 1", i-6))

local iIloc = i
n, t = readTag()
print(format("%4X:    [%2X] %s", i-8, n, t))
assert(t == "iloc")
assert(readIntBE() == 0) -- iloc version(1)&flags(3)
assert(readByte() == 0x44) -- offset_size,length_size
assert(readByte() == 0) -- base_offset_size,reserved
local item = readShortBE() -- item_count
print(format("%4X:         0, 0x44, 0, %X", i-8, item))
assert(item == 1 or item == 2) -- noAlpha or hasAlpha
local iColor, nColor, iAlpha, nAlpha
for j = 1,item do
	assert(readShortBE() == j) -- item_ID
	assert(readShortBE() == 0) -- data_reference_index
	assert(readShortBE() == 1) -- extent_count
	if j == 1 then
		iColor = readIntBE()
		nColor = readIntBE()
		print(format("%4X:         1: 0, 1, pos=%X, len=%X", i - 14, iColor, nColor))
	else
		iAlpha = readIntBE()
		nAlpha = readIntBE()
		print(format("%4X:         2: 0, 1, pos=%X, len=%X", i - 14, iAlpha, nAlpha))
	end
end
assert(i - iIloc == n)

local iIinf = i
n, t = readTag()
print(format("%4X:    [%2X] %s", i-8, n, t))
assert(t == "iinf")
assert(readIntBE() == 0) -- iinf version(1)&flags(3)
item = readShortBE() -- entry_count
print(format("%4X:         0, %X", i-6, item))
assert(item == 1 or item == 2) -- noAlpha or hasAlpha
for j = 1,item do
	local nn
	local iInfe = i
	nn, t = readTag()
	print(format("%4X:       [%2X] %s", i-8, nn, t))
	assert(t == "infe")
	assert(readIntBE() == 0x02000000) -- infe version(1)&flags(3)
	assert(readShortBE() == j) -- item_ID
	assert(readShortBE() == 0) -- item_protection_index
	assert(readStr(4) == "av01") -- item_type
	if j == 1 then
		assert(readStr() == "Color") -- item_name
		print(format("%4X:            1: 0x20, 0, 1, 0, av01, Color", i-18))
	else
		assert(readStr() == "Alpha") -- item_name
		print(format("%4X:            2: 0x20, 0, 1, 0, av01, Alpha", i-18))
	end
	assert(i - iInfe == nn)
end
assert(i - iIinf == n)

if iAlpha then
	local iIref = i
	n, t = readTag()
	print(format("%4X:    [%2X] %s", i-8, n, t))
	assert(t == "iref")
	assert(readIntBE() == 0) -- iinf version(1)&flags(3)
	print(format("%4X:         0", i-4))
	local nn
	local iAuxl = i
	nn, t = readTag()
	print(format("%4X:       [%2X] %s", i-8, nn, t))
	assert(t == "auxl") -- irefType
	assert(readShortBE() == 2) -- from_item_ID
	assert(readShortBE() == 1) -- reference_count
	assert(readShortBE() == 1) -- to_item_ID
	print(format("%4X:            2, 1, 1", i-6))
	assert(i - iAuxl == nn)
	assert(i - iIref == n)
end

local iIprp = i
n, t = readTag()
print(format("%4X:    [%2X] %s", i-8, n, t))
assert(t == "iprp")

local nn
local iIpco = i
nn, t = readTag()
print(format("%4X:       [%2X] %s", i-8, nn, t))
assert(t == "ipco")

local nnn
local iIspe = i
nnn, t = readTag()
print(format("%4X:          [%2X] %s", i-8, nnn, t))
assert(t == "ispe")
assert(readIntBE() == 0) -- infe version(1)&flags(3)
local width = readIntBE()
local height = readIntBE()
print(format("%4X:               0, width=%d, height=%d", i-12, width, height))
assert(i - iIspe == nnn)

local iPixi = i
nnn, t = readTag()
print(format("%4X:          [%2X] %s", i-8, nnn, t))
assert(t == "pixi")
assert(readIntBE() == 0) -- infe version(1)&flags(3)
local channel = readByte()
assert(channel == 1 or channel == 3) -- num_channels
print(format("%4X:               0, channel=%X:...", i-5, channel))
for j = 1,channel do
	assert(readByte() == 8) -- bits_per_channel
end
assert(i - iPixi == nnn)

local iAv1C = i
nnn, t = readTag()
print(format("%4X:          [%2X] %s", i-8, nnn, t))
assert(t == "av1C")
assert(readByte() == 0x81) -- marker(1b)&version(7b)
local b = readByte() -- seq_profile(3b)&seq_level_idx_0(5b)
assert(b == 0 or b == 0x20 or b == 0x40) -- 0:400/420/alpha; 0x20:444; 0x40:422
local b = readByte() -- seq_tier_0(1b)&high_bitdepth(1b)&twelve_bit(1b)&monochrome(1b)&chroma_subsampling_x(1b)&chroma_subsampling_y(1b)&chroma_sample_position(2b)
assert(b == 0 or b == 0x08 or b == 0x0c or b == 0x1c) -- 0:444; 0x08:422; 0x0c:420; 0x1c:400/alpha
assert(readByte() == 0) -- reserved(3)&initial_presentation_delay_present(1)&reserved(4)
assert(i - iAv1C == nnn)
local is400 = b == 0x1c

local iColr = i
nnn, t = readTag()
print(format("%4X:          [%2X] %s", i-8, nnn, t))
assert(t == "colr")
assert(readStr(4) == "nclx") -- colour_type
assert(readShortBE() == 1) -- colour_primaries
assert(readShortBE() == 13) -- transfer_characteristics
assert(readShortBE() == 6) -- matrix_coefficients
assert(readByte() == 0x80) -- full_range_flag(1)&reserved(7)
assert(i - iColr == nnn)

if iAlpha then
	if not is400 then
		local iPixi = i
		nnn, t = readTag()
		print(format("%4X:          [%2X] %s", i-8, nnn, t))
		assert(t == "pixi")
		assert(readIntBE() == 0) -- infe version(1)&flags(3)
		local channel = readByte()
		assert(channel == 1 or channel == 3) -- num_channels
		for j = 1,channel do
			assert(readByte() == 8) -- bits_per_channel
		end
		assert(i - iPixi == nnn)

		local iAv1c = i
		nnn, t = readTag()
		print(format("%4X:          [%2X] %s", i-8, nnn, t))
		assert(t == "av1C")
		assert(readByte() == 0x81) -- marker(1b)&version(7b)
		local b = readByte() -- seq_profile(3b)&seq_level_idx_0(5b)
		assert(b == 0 or b == 0x20 or b == 0x40) -- 0:400/420/alpha; 0x20:444; 0x40:422
		local b = readByte() -- seq_tier_0(1b)&high_bitdepth(1b)&twelve_bit(1b)&monochrome(1b)&chroma_subsampling_x(1b)&chroma_subsampling_y(1b)&chroma_sample_position(2b)
		assert(b == 0x08 or b == 0x0c or b == 0x1c or b == 0x20) -- 0x08:422; 0x0c:420; 0x1c:400/alpha; 0x20:444
		assert(readByte() == 0) -- reserved(3)&initial_presentation_delay_present(1)&reserved(4)
		assert(i - iAv1c == nnn)
	end

	local iAuxC = i
	nnn, t = readTag()
	print(format("%4X:          [%2X] %s", i-8, nnn, t))
	assert(t == "auxC")
	assert(readIntBE() == 0) -- infe version(1)&flags(3)
	assert(readStr() == "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha") -- aux_type
	assert(i - iAuxC == nnn)
end
assert(i - iIpco == nn)

local iIpma = i
nn, t = readTag()
print(format("%4X:        [%2X] %s", i-8, nnn, t))
assert(t == "ipma")
assert(readIntBE() == 0) -- infe version(1)&flags(3)
local ec = readIntBE()
assert(ec == 1 or ec == 2) -- entry_count

assert(readShortBE() == 1) -- item_ID
local ac = readByte()
assert(ac == 4) -- association_count
--	for k = 1,ac do
assert(readByte() == 1)
assert(readByte() == 2)
assert(readByte() == 0x83) -- essential(1b)&property_index(7b)
assert(readByte() == 4)
--	end
if iAlpha then
	assert(readShortBE() == 2) -- item_ID
	local ac = readByte()
	assert(ac == 4) -- association_count
	--	for k = 1,ac do
	assert(readByte() == 1)
	if is400 then
		assert(readByte() == 2)
		assert(readByte() == 0x83) -- essential(1b)&property_index(7b)
		assert(readByte() == 5)
	else
		assert(readByte() == 5)
		assert(readByte() == 0x86) -- essential(1b)&property_index(7b)
		assert(readByte() == 7)
	end
	--	end
end
assert(i - iIpma == nn)
assert(i - iMeta == nMeta)
print(format("%4X:", i))
